package com.sc.nft.admin.controller;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sc.nft.admin.request.*;
import com.sc.nft.annotation.SysMemberLogin;
import com.sc.nft.dao.*;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.TmpCollectionVO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.MetaChainHepler;
import com.sc.nft.mapper.UserCollectionMapper;
import com.sc.nft.service.UserCollectionService;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.sup.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/admin/chain")
@Api(tags = "后台玛特联盟链接口")
public class AdminMetaChainController {

    private final DigitalCollectionDao digitalCollectionDao;
    private final Redisson redisson;
    private final MetaChainHepler metaChainHepler;
    private final CollectionChainInitDetailsLogDao collectionChainInitDetailsLogDao;
    private final UserChainInfoDao userChainInfoDao;
    private final UserCollectionDao userCollectionDao;
    private final UserCollectionChainLogDao userCollectionChainLogDao;
    private final UserCollectionMapper userCollectionMapper;
    private final UserCollectionService userCollectionService;
    private final CollecionLinkContrastRecordsDao collecionLinkContrastRecordsDao;



    @PostMapping(value = "/deployNFT", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "藏品合约部署", notes = "藏品合约部署", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    public Result deployNFT(@Valid @RequestBody IdRequest request) {
        DigitalCollection collection = digitalCollectionDao.getById(request.getId());

        if(ObjectUtil.isNull(collection)){
            return Result.fail("藏品不存在");
        }

        if(collection.getContractStatus() == CollectionContractStatusEnum.DEPLOYING){
            return Result.fail("合约正在部署中,请勿重复操作!");
        }

        if(collection.getContractStatus() == CollectionContractStatusEnum.DEPLOY_SUCCESS){
            return Result.fail("合约已部署,请勿重复操作!");
        }

        if(collection.getOriginalType() == CollectionOriginalTypeEnum.MATE_WALLET){
        Assert.notNull(
          collecionLinkContrastRecordsDao.getOneByCollectionId(collection.getId()),
          () -> new GlobalRunTimeException("此藏品未关联外部藏品"));
        }

        String key = RedisKeyEnum.COLLECTION_DEPLOY_NFT_LOCK.getKey() + collection.getId();
        RLock lock = redisson.getLock(key);
        try {
            boolean res = lock.tryLock(1, TimeUnit.SECONDS);
            if (res) {
                String transactionHash = metaChainHepler.deployNFT(collection);
                Assert.isTrue(digitalCollectionDao.updateDeployTransactionHashAndContractStatusById(collection.getId(),transactionHash,CollectionContractStatusEnum.DEPLOYING),()-> new GlobalRunTimeException("藏品表更新失败!"));
            }else {
                return Result.fail(ErrorCode.LATER_TRY);
            }
        } catch (InterruptedException e) {
            return Result.fail("藏品上链中,请勿重复提交");
        } finally {
            lock.unlock();
        }
        return Result.success();
    }

    @PostMapping(value = "/getMetaData", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "查询数字商品元数据(不用对接)", notes = "查询数字商品元数据", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    public Result getMetaData(@Valid @RequestBody MetaDataRequest request) {
        CollectionChainInitDetailsLog collectionChainInitDetailsLog = collectionChainInitDetailsLogDao.getOneByCollectionIdAndUuid(request.getCollectionId(), request.getCollectionUuid());
        return Result.success(metaChainHepler.getMetaData(collectionChainInitDetailsLog.getContractAddress(),collectionChainInitDetailsLog.getTokenId()));
    }

    @PostMapping(value = "/updateMetaDataBatchByCollectionId", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "修改某个种类链上商品元数据(不用对接)", notes = "查询数字商品归属", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    public Result updateOneMetaData(@RequestBody IdRequest request) {

        DigitalCollection digitalCollection = digitalCollectionDao.getById(request.getId());
        Map<String, String> metaData = Maps.newHashMap();
        metaData.put("name", digitalCollection.getFirstTitle());
        metaData.put("url", digitalCollection.getCreationContent());

        log.info("藏品{}开始初始化，总数量{}",digitalCollection.getFirstTitle(),digitalCollection.getSumCount());
        int count = 1;
        int begin = 0;
        int end = 100000;
        log.info("仓库地址处理开始");
        for (int i = 1; i <= 10; i++) {
            begin = (i - 1) * end;
            log.info(count+"次");
            log.info("分页查询开始:{},结束:{}",begin,end);
            log.info("剩余数量:{}",(digitalCollection.getSumCount()-count++));

            List<CollectionChainInitDetailsLog> logs = collectionChainInitDetailsLogDao.getPageListByCollectionIdAndPage(digitalCollection.getId(), begin, end);
            for (CollectionChainInitDetailsLog collectionChainInitDetailsLog : logs) {
                metaChainHepler.updateMetaData(collectionChainInitDetailsLog ,metaData);
                log.info("剩余数量:{}",(digitalCollection.getSumCount()-count++));
            }
        }

        return Result.success();
    }
    @PostMapping(value = "/updateMetaData", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "修改链上商品元数据(不用对接)", notes = "查询数字商品归属", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    public Result updateMetaData(@RequestBody IdRequest request) {

        DigitalCollection digitalCollection = digitalCollectionDao.getById(request.getId());
        log.info("藏品{}开始初始化，总数量{}",digitalCollection.getFirstTitle(),digitalCollection.getSumCount());
        int count = 1;
        List<CollectionChainInitDetailsLog> logs = collectionChainInitDetailsLogDao.getListByCollectionId(digitalCollection.getId());
        for (CollectionChainInitDetailsLog collectionChainInitDetailsLog : logs) {
            Map<String, String> metaData = Maps.newHashMap();
            metaData.put("name", digitalCollection.getFirstTitle());
            metaData.put("url", digitalCollection.getCreationContent());
            metaChainHepler.updateMetaData(collectionChainInitDetailsLog ,metaData);
            log.info("剩余数量:{}",(digitalCollection.getSumCount()-count++));
        }
        return Result.success();
    }
    @PostMapping(value = "/updateMetaDataBatch", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "批量修改链上商品元数据(不用对接)", notes = "查询数字商品归属", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    public Result updateMetaDataBatch(@Valid @RequestBody UpdateMetaDataRequest request) {

        List<CollectionChainInitDetailsLog> list = collectionChainInitDetailsLogDao.getListByCollectionId(request.getId());

        List<Integer> tokenIds = Lists.newArrayList();
        List<Map> metadatas = Lists.newArrayList();

        list.stream().map(CollectionChainInitDetailsLog::getTokenId).forEach(tokenid->{
            tokenIds.add(tokenid);

            Map<String, String> metaData = Maps.newHashMap();
            metaData.put("name", request.getProductName());
            metaData.put("price", request.getPrice());
//            metaData.put("url", request.getUrl());
            metadatas.add(metaData);
        });
        return Result.success(metaChainHepler.updateMetaDataBatch(request.getContractAddress(),tokenIds,metadatas));
    }

    @PostMapping(value = "/getNFTOwner", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "查询数字商品归属(不用对接)", notes = "查询数字商品归属", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
//    @SysMemberLogin
    public Result getNFTOwner(@Valid @RequestBody NFTOwnerRequest request) {
        return Result.success(metaChainHepler.getNFTOwner(request.getContractAddress(),request.getTokenId()));
    }

    @PostMapping(value = "/transferLocal", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "执行链交易(仅执行链)", notes = "执行链交易(仅执行链)", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
//    @SysMemberLogin
    public Result transferLocal(@Valid @RequestBody TransferLocalRequest request) {
        metaChainHepler.transferLocal(request.getPrivateKey(),request.getContractAddress(),request.getTokenId(),request.getToAddress());
        return Result.success();
    }

    @PostMapping(value = "/tmp", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "临时处理", notes = "临时处理", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
//    @SysMemberLogin
    public Result tmp() {
        List<UserCollection> collections = userCollectionMapper.tmp1();
        for (UserCollection collection : collections) {
            String nftOwner = metaChainHepler.getNFTOwner(collection.getChainUrl(), collection.getNumbering());
            UserChainInfo byUserId = userChainInfoDao.getByUserId(collection.getUserId());
            if(StrUtil.equals(byUserId.getAddress(),nftOwner)){
                collection.setStatus(ChainStatusEnum.ONLINE);
                UserCollectionChainLog byCollectionId = userCollectionChainLogDao.getByCollectionId(collection.getId());
                byCollectionId.setStatus(UserChainLogStatusEnum.SUCCESS);
                byCollectionId.updateById();
                collection.updateById();
            }else{
                log.info("用户藏品编号{}不一致",collection.getId());
            }


        }
        return Result.success();
    }
    @PostMapping(value = "/getBalance", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "查询地址余额(不用对接)", notes = "查询地址余额", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    public Result getBalance(@Valid @RequestBody GetBalanceRequest request) {
        UserChainInfo byAddr = userChainInfoDao.getByAddr(request.getAddress());
        Assert.isTrue(ObjectUtil.isNotNull(byAddr),()-> new GlobalRunTimeException("地址不存在"));
        return Result.success(metaChainHepler.getBalance(request.getAddress()));
    }



    @PostMapping(value = "/updateFailChainData", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "修复错误链数据(不用对接)", notes = "修复错误链数据", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    public Result updateFailChainData() {
        List<TmpCollectionVO> tmpData = collectionChainInitDetailsLogDao.getTmpData();
        String adminAddress="0xd7e49c82d11ecb079cb2d150bfa30cce12d3b364";
        String contractAddress="0xfa9e0174defbb025a720470f1cd66cfd70609b59";
        int i =0;
        int j =0;

        for (TmpCollectionVO tmpDatum : tmpData) {
            String nftOwner = metaChainHepler.getNFTOwner(contractAddress, tmpDatum.getId().intValue());
            if(StrUtil.equals(nftOwner,adminAddress)){
                CollectionChainInitDetailsLog chainInitDetailsLog = collectionChainInitDetailsLogDao.getById(tmpDatum.getOldId());
                chainInitDetailsLog.setStatus(UserChainLogStatusEnum.SUCCESS);
                chainInitDetailsLog.setDealStatus(CollectionChainDealStatusEnum.TRADEABLE);
                chainInitDetailsLog.setNumbering(tmpDatum.getId().intValue());
                chainInitDetailsLog.setTokenId(tmpDatum.getId().intValue());
                collectionChainInitDetailsLogDao.updateById(chainInitDetailsLog);
                log.info("成功"+i+++"条");
            }else{
                log.info("失败"+j+++"条");
            }
        }
        return Result.success( );
    }


    @PostMapping(value = "/transferBalance", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "转账代币(不用对接)", notes = "查询地址余额", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    public Result transferBalance(@Valid @RequestBody TransferBalanceRequest request) {
        UserChainInfo fromAddr = userChainInfoDao.getByAddr(request.getFromAddress());
        Assert.isTrue(ObjectUtil.isNotNull(fromAddr),()-> new GlobalRunTimeException("转出地址不存在"));

        UserChainInfo toAddr = userChainInfoDao.getByAddr(request.getToAddress());
        Assert.isTrue(ObjectUtil.isNotNull(toAddr),()-> new GlobalRunTimeException("转入地址不存在"));

        return Result.success(metaChainHepler.transferBalance(fromAddr.getUserKey(),toAddr.getAddress(),request.getAmount()));
    }


    @PostMapping(value = "/recycleUserCollectionIds", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "回收指定藏品", notes = "回收指定藏品", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    @Transactional(rollbackFor = Exception.class)
    public Result recycleUserCollectionIds(@Valid @RequestBody RecycleUserCollectionByIds request) {

        //劳动最光荣
        List<UserCollection> userCollections =  userCollectionDao.getCollectionListByIds(request.getIds());
        //处理藏品(转增到平台账户)
        userCollectionService.recoverCollections(userCollections);

        return Result.success();
    }


    @PostMapping(value = "/destroyUserCollectionIds", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "销毁指定藏品", notes = "销毁指定藏品", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    @Transactional(rollbackFor = Exception.class)
    public Result destroyUserCollectionIds(@Valid @RequestBody RecycleUserCollectionByIds request) {

        //要销毁的藏品
        List<UserCollection> userCollections =  userCollectionDao.getCollectionListByIds(request.getIds());
        //销毁
        userCollectionService.ecologyDestroyCollections(userCollections);

        return Result.success();
    }


//    @PostMapping(value = "/temporarilyRecoverCollections", produces = MediaType.APPLICATION_JSON_VALUE)
//    @ApiOperation(value = "回收指定藏品-扬帆起航(临时)", notes = "回收指定藏品-扬帆起航(临时)", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
//    @SysMemberLogin
//    @Transactional(rollbackFor = Exception.class)
//    public Result temporarilyRecoverCollections() {
////    public Result recycleUserCollectionIds_test(@Valid @RequestBody RecycleUserCollectionByIds request) {
//        //获取历史未销毁的藏品
//        List<UserCollection> userCollections =  userCollectionDao.getGoalCollection();
//        log.info("总记录数:{}",userCollections.size());
//
//        //处理藏品(转增到平台账户)
//        userCollectionService.temporarilyRecoverCollections(userCollections);
//
//        return Result.success();
//    }

    @PostMapping(value = "/supplementaryChainRecord", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "补充链记录", notes = "补充链记录", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    @Transactional(rollbackFor = Exception.class)
    public Result supplementaryChainRecord(@Valid @RequestBody IdRequest request) {
        //用户藏品id
        supplementaryChainRecord(request.getId());
        return Result.success();
    }
    @PostMapping(value = "/batchSupplementaryChainRecord", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation(value = "批量补充链记录", notes = "批量补充链记录", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @SysMemberLogin
    @Transactional(rollbackFor = Exception.class)
    public Result batchSupplementaryChainRecord(@Valid @RequestBody IdRequest request) {
        //藏品id
        DigitalCollection digitalCollection = digitalCollectionDao.getById(request.getId());
        //查询链记录表不存在的用户藏品信息
        List<UserCollection> list = userCollectionDao.getNotInUserCollectionChaingList(digitalCollection.getId());

        log.info(StrUtil.format("批量补充链记录数量:{},藏品id:{}",list.size(),digitalCollection.getId()));
        for (UserCollection userCollection : list) {
            log.info("用户藏品表id:{},藏品id:{},藏品编号:{}",ObjectUtil.isNull(userCollection) ? "用户藏品不存在" : userCollection.getId(),ObjectUtil.isNull(userCollection) ? "用户藏品不存在" : userCollection.getCollectionId(),ObjectUtil.isNull(userCollection) ? "用户藏品不存在":userCollection.getNumbering());
            supplementaryChainRecord(userCollection.getId());
        }


        return Result.success();
    }



    private void supplementaryChainRecord(Long userCollectionId){
        UserCollection userCollection = userCollectionDao.getById(userCollectionId);
        if(ObjectUtil.isNull(userCollection)){
            throw new GlobalRunTimeException("用户藏品不存在");
        }

        CollectionChainInitDetailsLog collectionChainInitDetailsLog = collectionChainInitDetailsLogDao.getOneByCollectionIdAnTokenId(userCollection.getCollectionId(),userCollection.getNumbering());
        if(ObjectUtil.isNull(collectionChainInitDetailsLog)){
            throw new GlobalRunTimeException("藏品仓库表不存在");
        }

        if(collectionChainInitDetailsLog.getStatus() != UserChainLogStatusEnum.SUCCESS){
            throw new GlobalRunTimeException(StrUtil.format("藏品仓库表状态不正确,状态:{},id:{}",collectionChainInitDetailsLog.getStatus().getDescription(),collectionChainInitDetailsLog.getId()));
        }

        if(collectionChainInitDetailsLog.getDealStatus() != CollectionChainDealStatusEnum.TRADEABLE){
            throw new GlobalRunTimeException(StrUtil.format("藏品仓库表可交易状态不正确,状态:{},id:{}",collectionChainInitDetailsLog.getDealStatus().getDescription(),collectionChainInitDetailsLog.getId()));
        }


        collectionChainInitDetailsLogDao.updateDealStatusById(collectionChainInitDetailsLog.getId(),CollectionChainDealStatusEnum.TRADED);

        UserChainInfo toUser = userChainInfoDao.getByUserId(userCollection.getUserId());
        UserChainInfo fromUser = userChainInfoDao.getByUserId(1l);

        UserCollectionChainLog userCollectionChainLog = new UserCollectionChainLog();
        userCollectionChainLog.setUserId(userCollection.getUserId());
        userCollectionChainLog.setDaoId(0L);
        userCollectionChainLog.setUserCollectionId(userCollection.getId());
        userCollectionChainLog.setCollectionType(userCollection.getType());
        userCollectionChainLog.setType(UserChainTypeEnum.TRANSFER);
        userCollectionChainLog.setStatus(UserChainLogStatusEnum.WAIT);
        userCollectionChainLog.setCollectionId(userCollection.getCollectionId());
        userCollectionChainLog.setContractAddress(collectionChainInitDetailsLog.getContractAddress());
        userCollectionChainLog.setDeployTransactionHash(collectionChainInitDetailsLog.getDeployTransactionHash());
        userCollectionChainLog.setTransactionHash(StrUtil.EMPTY);
        userCollectionChainLog.setProductId(collectionChainInitDetailsLog.getCollectionUuid());
        userCollectionChainLog.setAddress(toUser.getAddress());
        userCollectionChainLog.setTokenId(collectionChainInitDetailsLog.getTokenId());
        userCollectionChainLog.setFromUserId(fromUser.getUserId());
        userCollectionChainLog.setFromUserCollectionId(0L);
        userCollectionChainLog.setFromUserAddress(fromUser.getAddress());
        userCollectionChainLog.setToUserId(toUser.getUserId());
        userCollectionChainLog.setToUserCollectionId(userCollection.getId());
        userCollectionChainLog.setToUserAddress(toUser.getAddress());
        userCollectionChainLog.setCollectionChainId(collectionChainInitDetailsLog.getId());
        userCollectionChainLog.insert();
    }
}
